﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Game500.ClientServerCommon.Src;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Game500.Client.Src.Logic
{
  public static class ObjectDescriptorUtils
  {
    public static void UpdateFromObjectDescriptor(this Renderable renderable, ObjectDescriptor descriptor)
    {
      renderable.Position = descriptor.Position ?? renderable.Position;
      renderable.Rotation = descriptor.Rotation ?? renderable.Rotation;
      renderable.Visible = descriptor.Visibility ?? renderable.Visible;
      if (descriptor.Flip.HasValue)
      {
        renderable.ScaleY = descriptor.Flip == true ? -1 : 1;
      }
    }
  }

  public class ObjectManager
  {
    private readonly Camera2D myCamera;
    private readonly ClientPrototypeManager myClientPrototypeManager;
    private readonly GraphicsDeviceManager myGraphicsDeviceManager;
    private readonly Dictionary<int, Renderable> myObjects = new Dictionary<int, Renderable>();
    private readonly Renderer myRenderer;
    private readonly ResourceManager myResourceManager;
    private readonly FxManager myFxManager;
    private readonly SpriteBatch mySpriteBatch;
    private Background myBackground;

    private float myFrameTime = 1.0f;
    private GameInfo myGameInfo;

    public ObjectManager(FxManager fxManager, GraphicsDeviceManager graphicsDeviceManager, ResourceManager resourceManager)
    {
      myFxManager = fxManager;
      myGraphicsDeviceManager = graphicsDeviceManager;
      myResourceManager = resourceManager;
      myCamera = new Camera2D(myGraphicsDeviceManager.GraphicsDevice.Viewport.Width, myGraphicsDeviceManager.GraphicsDevice.Viewport.Height);

      myClientPrototypeManager = new ClientPrototypeManager(myResourceManager, this);
      mySpriteBatch = new SpriteBatch(myGraphicsDeviceManager.GraphicsDevice);
      myRenderer = new Renderer(mySpriteBatch, myCamera, myResourceManager.Font);
    }

    public Renderable Player { get; private set; }

    public IEnumerable<KeyValuePair<int, Renderable>> Objects
    {
      get { return myObjects; }
    }

    public void StartGame(GameInfo info, Map map)
    {
      myGameInfo = info;
      myBackground = new Background(myResourceManager, map);

      myCamera.SetConstrains(new Vector2(0, -myBackground.LowBound), new Vector2(map.MapWidth, map.MapHeight + map.YOffset));
    }

    public void Update(SceneFrame frame, GameTime gameTime)
    {
      myFxManager.Update(gameTime);
      if (Player != null) myFxManager.MuteEngine = !Player.Visible;

      foreach (ObjectDescriptor descriptor in frame.DataSection)
      {
        Renderable entity;
        if (descriptor.Id == 2) continue;
        if (!myObjects.TryGetValue(descriptor.Id, out entity))
        {
          entity = CreateObject(descriptor.Id, descriptor.ResourceId);
          Debug.Assert(entity != null);
          myObjects[descriptor.Id] = entity;
        }
        entity.UpdateFromObjectDescriptor(descriptor);
        var plane = entity as Plane;
        if (plane != null)
        {
          plane.Score = frame.Score[descriptor.Id];
        }

        if (descriptor.Id == myGameInfo.PlayerId)
        {
          var player = entity as Plane;
          Debug.Assert(player != null, "player != null");
          player.IsOtherPlayer = false;
          Player = entity;
        }
      }

      var hashSet = new HashSet<int>(frame.DataSection.Select(x => x.Id));

      foreach (var entity in myObjects.ToList())
      {
        if (!hashSet.Contains(entity.Key)) myObjects.Remove(entity.Key);
      }
    }

    private Renderable CreateObject(int id, int resourceId)
    {
      string resId = MakeFakeResourceId(id, resourceId);
      return myClientPrototypeManager.GetEntity(resId);
    }

    private string MakeFakeResourceId(int id, int resourceId)
    {
      if (resourceId == 1) return "clouds";
      if (resourceId == 3) return "shell";
      if (resourceId == 0)
      {
        var builder = new StringBuilder("plane#");
        switch (id % 4)
        {
          case 0:
            builder.Append("blue");
            break;
          case 1:
            builder.Append("red");
            break;
          case 2:
            builder.Append("yellow");
            break;
          case 3:
            builder.Append("green");
            break;
        }
        return builder.ToString();
      }
      return "error";
    }

    public void UpdateAll(float dt)
    {
      if (Player == null) return;
      myCamera.Position = new Vector2(Player.Position.X, Player.Position.Y);
      myFrameTime = dt;
    }

    public void RenderAll()
    {
      if (Player == null) return;

      myRenderer.BeginFrame();

      if (Math.Abs(myFrameTime) > float.Epsilon)
      {
      }

      myBackground.Render(myRenderer);

      foreach (Renderable renderable in myObjects.Values)
      {
        renderable.Render(myRenderer);
      }

      myFxManager.Draw(myRenderer);
      myRenderer.EndFrame();
    }
  }
}